<?php
declare (strict_types=1);

namespace app\common\service;

use app\common\helper\util\FileUtil;
use app\common\logic\Attach;
use eduline\upload\Client as FileClient;
use think\{File, Model, Request, Response};
use think\exception\{FileException, ValidateException};

/**
 * 权限规则接口类
 */
class Upload extends BaseService
{
    /**
     * 快传文件
     *
     * Author   Martinsun<syh@sunyonghong.com>
     * Date 2020-03-13
     *
     * @param Request $request [description]
     * @return Response [type]                                  [description]
     */
    public function fast(Request $request)
    {
        $md5 = $request->get('md5');
        if (!$md5) {
            return $this->parseToData(['error_code' => 10409], 0, '请上传文件');
        }
        // 查询文件是否已经上传过,且目前资源存在本地,包含假删除的数据
        $attach = app(Attach::class)->withTrashed()->where('filemd5', strtolower($md5))->where('stock', 'local')->order('id', 'desc')->findOrEmpty();
        if ($attach->isEmpty()) {
            return $this->parseToData(['error_code' => 10409], 0, '请上传文件');
        }
        // 本地存在文件,直接复制
        $savepath  = app(Attach::class)->getUploadPath();
        $extension = $attach->extension;
        $savename  = md5(microtime()) . '.' . $extension;
        $outfile   = $savepath . date('Ymd') . DIRECTORY_SEPARATOR . $savename;
        if (is_file($attach->filepath) && FileUtil::copyFile($attach->filepath, $outfile, true)) {
            // 载入
            $file     = new File($outfile);
            $filename = $request->get('name', $attach->filename);
            $isTemp   = $request->get('is_temp/b', false);
            $info     = app(Attach::class)->saveAttach($file, ['filename' => $filename], $isTemp);
            if ($info) {
                $data = $info->append(['fileurl'])
                    ->hidden(['filedata', 'bucket', 'stock', 'to_stock', 'savename', 'savepath'])->toArray();

                $data['create_time'] = strtotime($data['create_time']);

                return $this->parseToData($data, 1, '上传完成');
            }

            // 如果失败,继续执行则触发上传
        }

        return $this->parseToData(['error_code' => 10409], 0, '请上传文件');
    }

    /**
     * 检测文件片段是否存在
     *
     * Author   Martinsun<syh@sunyonghong.com>
     * Date 2020-03-13
     *
     * @param Request $request [description]
     * @return Response [type]                                  [description]
     */
    public function checkChunk(Request $request)
    {
        // 检测切片是否已经上传
        $savepath   = app(Attach::class)->getUploadPath('filechunks/');
        $chunk_file = $savepath . $request->get('md5') . '_' . $request->get('chunk') . '.part';
        if (file_exists($chunk_file) && filesize($chunk_file) == $request->get('size')) {
            return $this->parseToData(['exist' => 1], 1, '文件片段已存在');
        } else {
            return $this->parseToData(['error_code' => 10409], 0, '文件片段不存在');
        }
    }

    /**
     * 上传文件片段
     *
     * Author   Martinsun<syh@sunyonghong.com>
     * Date 2020-03-13
     *
     * @param Request $request [description]
     * @return Response [type]                                  [description]
     */
    public function uploadChunk(Request $request)
    {
        // 获取表单上传文件 例如上传了001.jpg
        $file = $request->file('file');
        if (!$file) {
            return $this->parseToData(['error_code' => 10409], 0, '请上传文件');
        }
        try {
            $name       = $request->post('md5') . '_' . $request->post('chunk') . '.part';
            $client     = new FileClient('local');
            $res        = $client->putFile('/filechunks', $file, $name);
            $res['ext'] = 'part';

            return $this->parseToData($res, 1, '片段上传完成');
        } catch (FileException $e) {
            // 上传失败获取错误信息
            return $this->parseToData(['error_code' => 10409], 0, '片段上传失败');
        }
    }

    /**
     * 合并分片文件
     *
     * Author   Martinsun<syh@sunyonghong.com>
     * Date 2020-03-13
     *
     * @param Request $request [description]
     * @return Response [type]                                  [description]
     */
    public function merageUploadChunks(Request $request)
    {
        $savepath  = app(Attach::class)->getUploadPath();
        $extension = strtolower(substr(strrchr($request->post('name'), '.'), 1));
        $savename  = md5(microtime()) . '.' . $extension;
        $outfile   = $savepath . date('Ymd') . DIRECTORY_SEPARATOR . $savename;
        if (!is_dir(dirname($outfile))) {
            @mkdir(dirname($outfile), 0777, true);
        }
        // 合并操作
        if (!$out = @fopen($outfile, "w+")) {
            return $this->parseToData(['error_code' => 10409], 0, '合并分片文件失败');
        }
        if (flock($out, LOCK_EX)) {
            $chunks    = $request->post('chunks');
            $chunkpath = app(Attach::class)->getUploadPath('filechunks/');
            $md5       = $request->post('md5');
            // $chunkSize = $request->post('chunkSize');
            for ($index = 0; $index < $chunks; $index++) {
                $f = iconv('UTF-8', 'GB2312', $chunkpath . $md5 . '_' . $index . '.part');
                if (!$in = @fopen($f, "r+")) {
                    break;
                }
                while ($buff = fread($in, 2048)) {
                    fwrite($out, $buff);
                }
                @fclose($in);
                @unlink($f);
            }
            flock($out, LOCK_UN);
        }
        @fclose($out);
        $isTemp = $request->post('is_temp/b', false);
        // 载入
        $file = new File($outfile);
        $info = app(Attach::class)->saveAttach($file, ['filename' => $request->post('name')], $isTemp);
        if ($info) {
            $data = $info->append(['fileurl'])->hidden(['filedata', 'bucket', 'stock', 'to_stock', 'savename', 'savepath'])->toArray();

            $data['create_time'] = strtotime($data['create_time']);

            return $this->parseToData($data, 1, '上传完成');
        } else {
            return $this->parseToData(['error_code' => 10409], 0, '上传出错');
        }
    }

    /**
     * 简单上传
     *
     * Author   Martinsun<syh@sunyonghong.com>
     * Date 2020-04-17
     *
     * @param Request $request [description]
     * @return Response [type]                                  [description]
     */
    public function putFile(Request $request)
    {
        // 获取表单上传文件 例如上传了001.jpg
        $file = $request->file('file');
        if (!$file) {
            return $this->parseToData(['error_code' => 10409], 0, '请上传文件');
        }
        $filename = $request->has('name', 'post') ? $request->post('name') : $file->getOriginalName();

        if (!$filename) {
            return $this->parseToData(['error_code' => 10409], 0, '文件名不正确');
        }
        $extension = strtolower(substr(strrchr($filename, '.'), 1));

        $savename = md5(microtime()) . '.' . $extension;
        $date     = date('Ymd');
        try {
            $info = $this->getInfo($date, $file, $savename, $filename);
            if ($info) {
                $data = $info->append(['fileurl'])->hidden(['filedata', 'bucket', 'stock', 'to_stock', 'savename', 'savepath'])->toArray();

                $data['create_time'] = strtotime($data['create_time']);

                return $this->parseToData($data, 1, '上传完成');
            } else {
                return $this->parseToData(['error_code' => 10409], 0, '上传出错');
            }
        } catch (ValidateException $e) {
            return $this->parseToData(['error_code' => 10409], 0, $e->getMessage());
        } catch (FileException $e) {
            // 上传失败获取错误信息
            return $this->parseToData(['error_code' => 10409], 0, $e->getMessage());
        }
    }

    /**
     * 简单上传
     *
     * Author   Martinsun<syh@sunyonghong.com>
     * Date 2020-04-17
     *
     * @param Request $request [description]
     * @return Response [type]                                  [description]
     */
    public function putEditorImage(Request $request)
    {
        // 获取表单上传文件 例如上传了001.jpg
        $file = $request->file('upload');
        if (!$file) {
            return Response::create(['error' => ['message' => '请上传文件']], 'json')->code(200);
        }
        $filename = $request->has('name', 'post') ? $request->post('name') : $file->getOriginalName();

        if (!$filename) {
            return Response::create(['error' => ['message' => '文件名不正确']], 'json')->code(200);
        }
        $extension = strtolower(substr(strrchr($filename, '.'), 1));

        $savename = md5(microtime()) . '.' . $extension;
        $date     = date('Ymd');
        try {
            $info = $this->getInfo($date, $file, $savename, $filename);
            if ($info) {
                $data = $info->append(['fileurl'])->hidden(['filedata', 'bucket', 'stock', 'to_stock', 'savename', 'savepath']);

                return Response::create(['url' => $data->getAttr('fileurl')], 'json')->code(200);
            } else {
                return Response::create(['error' => ['message' => '上传出错了']], 'json')->code(200);
            }
        } catch (FileException $e) {
            // 上传失败获取错误信息
            return Response::create(['error' => ['message' => $e->getMessage()]], 'json')->code(200);
        }
    }

    /**
     * getInfo
     * Author: 亓官雨树 <lucky.max@foxmail.com>
     * Date: 23/09/12
     *
     * @param        $date
     * @param        $file
     * @param string $savename
     * @param        $filename
     * @return Attach|array|false|Model|null
     */
    protected function getInfo($date, $file, string $savename, $filename)
    {
        $client = new FileClient('local');
        $client->putFile('/' . $date, $file, $savename);

        // 载入
        $savepath = app(Attach::class)->getUploadPath();

        $outfile = $savepath . $date . DIRECTORY_SEPARATOR . $savename;
        $file    = new File($outfile);
        return app(Attach::class)->saveAttach($file, ['filename' => $filename]);
    }

}
